package metodosRaicesEcuaciones;

import excepciones.RaizNoEncontradaExcepcion;
import funciones.FuncionX;

/**
 * <p>El metodo de biseccion busca las raices de una funcion f(x) dividiendo el
 *  intervalo [a,b] a la mitad y seleccionando el subintervalo que tiene la raiz.
 *  </p>
 *  <p>
 *  Verificando que f'(x) y f(x) son continuas en [a,b], f(a)*f(b)<0 y f'(x)!=0
 *  para todo x perteneciente a [a,b] se puede asegurar que el metodo converge a
 *  la raiz de f. Ya que f(a)*f(b)<0, f(a) y f(b) deben tener signos opuestos. Por
 *  el Teorema de Bolzano f debe tener al menos una raiz en [a,b].
 *  </p>
 *  <p>
 *  El metodo de biseccion divide al intervalo en dos partes tomando como punto
 *  medio r = (a+b)/2. Evaluando f(r) hay tres posibilidades:<br>
 *  1) f(r)=0 (r es raiz, el algoritmo devuelve el valor)<br>
 *  2) El signo de f(r) es igual al signo de f(b) (en este caso, b pasa a valer r)<br>
 *  3) El signo de f(r) es igual al signo de f(a) (a pasa a valer r)<br>
 *  De este modo el metodo se vuelve a aplicar en el intervalo donde hay un cambio
 *  de signo. <br>
 *  Los pasos se repiten hasta que f(r)=0 o hasta que se encuentra un valor
 *  de r menor al error tolerado.
 *  </p>
 *  <p>
 *  <center>
 * <img alt="Bisection"  height="250"  width="291"  
 * src="..\resources\Bisection_method.png" >
 * </center>
 * </p>
 */
public class Biseccion {
	
	private static final String METHOD_NAME = "Biseccion";

    /**
     * El metodo de Biseccion encuentra la raiz existente entre 2 valores conocidos.
     *
     * @param a          valor minimo del intervalo
     * @param b          valor maximo del intervalo
     * @param error      error tolerado
     * @param iterations iteraciones permitidas
     * @return raiz de la funcion
     * @throws RaizNoEncontradaExcepcion si no se encuentra la raiz o
     * si la funcion no cumple con las condiciones minimas necesarias
     */

    public static double findRoot(FuncionX f, double a, double b, double error, int iterations)
            throws RaizNoEncontradaExcepcion {
        int i = 1;
        double fa = f.eval(a); // evaluo f en a
        
        while (i <= iterations) {
            double r = a + (b - a) / 2; //punto medio del intervalo
            double fp = f.eval(r); //f evaluado en p
            //si fp es raiz o el intervalo es menos al error tolerado
            if (fp == 0 || (b - a) / 2 < error) {
                return r;
            }
            i += 1;
            //si fa y fp tienen el mismo signo
            if (fa * fp > 0) {
                a = r;
                fa = fp;
            } else {
                b = r;
            }
        }
        throw new RaizNoEncontradaExcepcion(METHOD_NAME);
    }
}
